package net.kazed.nextaction.database.backup;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.ProviderException;
import java.util.Date;
import java.util.List;

import net.kazed.android.inject.Autowired;
import net.kazed.android.inject.Component;
import net.kazed.nextaction.database.DatabaseHelper;
import net.kazed.nextaction.database.GtdContext;
import net.kazed.nextaction.database.Task;
import net.kazed.nextaction.database.TaskContext;
import net.kazed.nextaction.database.TaskDao;
import net.kazed.nextaction.database.serialize.BackupFile;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;

@Component("backupService")
public class BackupService {

   private Context context;
   private DatabaseHelper databaseHelper;
   private TaskDao taskDao;

   @Autowired
   public void setContext(Context context) {
      this.context = context;
   }

   @Autowired
   public void setDatabaseHelper(DatabaseHelper databaseHelper) {
      this.databaseHelper = databaseHelper;
   }

   @Autowired
   public void setTaskDao(TaskDao taskDao) {
      this.taskDao = taskDao;
   }

   public void restoreMerge(long backupId) {
      restore(backupId, false);
   }

   public void restoreReplace(long backupId) {
      restore(backupId, true);
   }

   public void copyToSdCard(long backupId) {
      String fileName = backupId + ".nab";
      File exportedFile = new File(Environment.getExternalStorageDirectory(), fileName);
      if (exportedFile.exists()) {
         throw new ExportFileExistsException("Export file already exists");
      }
      FileOutputStream output = null;
      FileInputStream input = null;
      try {
         output = new FileOutputStream(exportedFile);
         input = context.openFileInput(fileName);
         boolean copying = true;
         byte buffer[] = new byte[4096];
         while (copying) {
            int length = input.read(buffer);
            if (length < 0) {
               copying = false;
            } else {
               output.write(buffer, 0, length);
            }
         }
      } catch (IOException e) {
         throw new ProviderException("failed to copy backup file to SD card", e);
      } finally {
         if (output != null) {
            try {
               output.close();
            } catch (IOException e) {
            }
         }
         if (input != null) {
            try {
               input.close();
            } catch (IOException e) {
            }
         }
      }
   }
   
   private void restore(long backupId, boolean replace) {
      SQLiteDatabase db = databaseHelper.getWritableDatabase();
      db.beginTransaction();
      if (replace) {
         db.delete(DatabaseHelper.TABLE_CONTEXT, null, null);
         db.delete(DatabaseHelper.TABLE_TASK, null, null);
         taskDao.deleteAllTaskContexts();
      }

      try {
         RestoreDao restoreDao = new RestoreDaoImpl();
         String fileName = backupId + ".nab";
         BackupFile backupFile = new BackupFile(fileName);
         if (replace) { // TODO is the same, consolidate
            backupFile.restoreReplaceBackup(context, restoreDao, db);
         } else {
            backupFile.restoreMergeBackup(context, restoreDao, db);
         }
         databaseHelper.updateNumberOfTasksInAllContexts(db);
         databaseHelper.updateNumberOfSubTasksInAllTasks(db);
         db.setTransactionSuccessful();
      } finally {
         db.endTransaction();
      }
      context.getContentResolver().notifyChange(GtdContext.CONTENT_URI, null);
      context.getContentResolver().notifyChange(Task.CONTENT_URI, null);
   }

   public void delete(long backupId) {
      String fileName = backupId + ".nab";
         context.deleteFile(fileName);
   }
   
   public void createBackup() {
      SQLiteDatabase db = databaseHelper.getReadableDatabase();
      Date created = new Date();
      String fileName = Long.toString(created.getTime()) + ".nab";
      BackupDao backupDao = new BackupDaoImpl();
      BackupFile backupFile = new BackupFile(fileName);
      backupFile.createBackup(context, backupDao, db);
   }

   private class BackupDaoImpl implements BackupDao {

      @Override
      public List<TaskContext> getTaskContextsByTask(Integer taskId) {
         return taskDao.getTaskContexts(taskId);
      }
   }
   
   private class RestoreDaoImpl implements RestoreDao {

      @Override
      public void replaceTaskContexts(int taskId, List<Integer> contextIds) {
         taskDao.deleteTaskContextsByTask(taskId);
         for (Integer contextId : contextIds) {
            TaskContext taskContext = new TaskContext(taskId, contextId);
            taskDao.save(taskContext);
         }
      }
   }

}
